#include "../SypEngine.h"

#ifdef CHECKMEM
#include "../mmgr.h"
#endif
using namespace Syp;

CPUSkinning::CPUSkinning():Pass("CPUSkinning","Applies Skinning in software to a Dynamic IndexedMesh")
{

}

bool CPUSkinning::runnableOnHardware()
{
	return true;
}

void CPUSkinning::destroy()
{
	delete this;
}

void CPUSkinning::preDraw()
{
	MeshInstance* m = Renderer::getRenderer().getCurrentMI();
	IndexedMesh* im = static_cast<IndexedMesh*>(Renderer::getRenderer().getCurrentMesh());
	if(im->isSkinned() && im->getSkeleton())
	{///Check to see if MI not been transformed by one of it's other Renderable. If it has, m->AnimTime does not need updating
		if(m->animate)
		{//dont progress time if not animated
			if(m->lastTransformedOn != SypEngine::getSypEngine().getCurrFrameTime())
			{
				//Get time elapsed
				float timeElapsed = static_cast<float>(SypEngine::getSypEngine().getElapsedTime());
				//update 
				//(Mesh's relative time in frames)
				m->AnimTime += (timeElapsed * m->speed);
				m->lastTransformedOn = SypEngine::getSypEngine().getCurrFrameTime();

			} 
			//If animation is over, restart it
			//Possibly add other modes in future
			if(m->AnimTime > im->getSkeleton()->getAnimLength())
			{
				//SUppose our time is 306 and animlength is 100, our outstanding is 206 frames. 
				// 306 / 100 is 3
				// 306 - (3* 100) = 6

				m->AnimTime = m->AnimTime - (static_cast<int>((m->AnimTime / im->getSkeleton()->getAnimLength())) * im->getSkeleton()->getAnimLength());			
			}
		}
		//If mesh is already at this frame, return and avoid transforming
		if(im->getCurrAnimTrans() == m->AnimTime)
		{
			return;
		}	

		//---------------------------Begin actual Transforming-----------------------

		float interpolation = 0.0f;//float defining how much to interpolate by
		//Mesh is now being transformed to this time in seconds
		im->setCurrAnimTrans(m->AnimTime);

		PInt numBones = im->getSkeleton()->getNumBones();
	
		

		PInt boneCounter = 0;
		//for every bone
		while(boneCounter < numBones)
		{	          			
			im->transformBone(im->getSkeleton()->getBoneRef(boneCounter));
			//next bone
			++boneCounter;
		}
		//Now that we have our matrices, we do the mesh skinning based on our bone position/orientations. Since this can be done in the hardware, we let the renderer decide on how it should be done.

		AnimateBoneMesh(im);
	}

}

void CPUSkinning::AnimateBoneMesh(IndexedMesh* m)
{
	//PShortArray::iterator i = m->skinID.begin();
	//PShortArray::iterator end = m->skinID.end();
	//Temporary vertex counter
	PShort vertexCnt = 0;
	PShort numV = 0;
	//Actual offset in data array
	PInt vIndex = 0;
	Syp::Byte off = 0;
	//Software Processing starts here
	if(m->getVertType() == VNT )
	{
		off = 8;
	}
	else if(m->getVertType() == VN)
	{
		off = 6;
	}
	FloatArray& data = m->getDataRef();
	numV = static_cast<PShort>(data.size()/off);
	//std::cout<<"\nData Array : \n";
	//for(size_t i=0;i<data.size();++i)
	//{
	//	std::cout<<" "<<data[i];
	//}
	//std::cout<<"\nBakData Array : \n";
	//for(size_t i=0;i<data.size();++i)
	//{
	//	std::cout<<" "<<m->getBakDataRef()[i];
	//}
	
	//Resetting of vertices to 0
	for(PInt k=0;k<numV;++k)
	{
		//vertices
		data[ k*off ] = 0;
		data[ k*off + 1 ] = 0;
		data[ k*off + 2 ] = 0;
		//normals
		data[ k*off + 3 ] = 0;
		data[ k*off + 4 ] = 0;
		data[ k*off + 5 ] = 0;
	}
	//for every vertex 
	while (vertexCnt < numV )
	{
		//Find the index of the vertex
		PShort skinIndex =	m->getSkinIDRef()[vertexCnt];
		vIndex = vertexCnt * off;

		//Mulitply it with all the bones it is affected by
		//for every bone affecting this vertex
		for(PInt g = 0; g< m->getVWeightsRef()[skinIndex].BoneID.size();++g)
		{
			Bone& theBone = m->getSkeleton()->getBoneRef( m->getVWeightsRef()[skinIndex].BoneID[g] );
			PInt wat = m->getVWeightsRef()[skinIndex].BoneID[g];
			Math::MultVectorByMatrix(
				//Bone [  BoneID from VWeights used by this vertex     ]
				//Use backupdata for original
				//Use the weight for this bone
				//Store in mesh
				theBone.getMatrix(),
			//m->getBindPoseMatrix(),
				&m->getBakDataRef()[vIndex],
				m->getVWeightsRef()[skinIndex].weight[g],
				&m->getDataRef()[vIndex]);
			
			//Normals
			Math::MultNormalByMatrix(
				theBone.getMatrix(),
				//m->getBindPoseMatrix(),
				&m->getBakDataRef()[ vIndex + 3 ],
				m->getVWeightsRef()[skinIndex].weight[g],
				&m->getDataRef()[ vIndex + 3]);
		/*	std::cout<<"Nor : "<<m->getDataRef()[vIndex+3]<<" "<<m->getDataRef()[vIndex + 4]<<" "<<m->getDataRef()[vIndex + 5]<<"\n";
				std::cout<<"Orig : "<<m->getBakDataRef()[vIndex+3]<<" "<<m->getBakDataRef()[vIndex + 4]<<" "<<m->getBakDataRef()[vIndex + 5]<<"\n";*/


		}

		++vertexCnt;
	}

	//Submit changes
	Renderer::getRenderer().updateMesh(m);

}

void CPUSkinning::postDraw()
{

}
